"""
https://leetcode.cn/problems/combination-sum/description/

39. 组合总和
已解答
中等
相关标签
相关企业
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ，找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ，并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同，则两种组合是不同的。 

对于给定的输入，保证和为 target 的不同组合数少于 150 个。

 

示例 1：

输入：candidates = [2,3,6,7], target = 7
输出：[[2,2,3],[7]]
解释：
2 和 3 可以形成一组候选，2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选， 7 = 7 。
仅有这两种组合。
示例 2：

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3：

输入: candidates = [2], target = 1
输出: []
 

提示：

1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40

"""
from typing import List


class Solution:
    """
    从数学的思想上考虑，假定候选数字是从n1到np
    假定f(m)表示和是m的结果列表，则使用ad(f(m),n)表示将结果列表的每个数组后面都加一个n，很明显，ad(f(m),n)是f(m+n)的一个子集
    则f(target)=ad(f(target-n1),n1)+ad(f(target-n2),n2)+...+ad(f(target-np),np)
    另有：
    f(0)=[[]]
    f(负数)=[]
    基于以上原理，可以用合并方法获得

    但需考虑去重

    """
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def ad(num:int,last_cand:int)->List[List[int]]:
            if num<0:
                return []
            if num==0:
                return [[]]
            result = []
            for cand in candidates:
                if cand<last_cand:
                    continue
                last_res=ad(num-cand,cand)
                for last_each in last_res:
                    last_each.insert(0,cand)
                    result.append(last_each)

            return result
        
        res=ad(target,0)

        return res
    
if __name__ =='__main__':
    s=Solution()
    candidates = [2]
    target = 1
    res=s.combinationSum(candidates,target)
    print(res)